import lombok.Getter;
import configClient.Environment;
import configClient.TccConfigClient;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.io.AbstractResource;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.commons.lang3.StringUtils.isBlank;

/**
 * BaseTccPropertySource 就是tcc上面一个key形成的属性源 这里 SpringBoot1.x版本 和 SpringBoot2.x loadProperties 实现不同。
 * 为什么要自己搞一个 BaseTccPropertySource ? 目的在于关联 tccConfigClient 。 TccWatchAutoConfiguration会拿到所有 BaseTccPropertySource
 * 给其对应的 tccConfigClient 添加监听器。
 *
 * EnumerablePropertySource 是 Spring 框架中表示属性源的一个抽象基类。从 PropertySource 类继承而来，并添加了枚举的能力。
 * 这意味着 EnumerablePropertySource 类的实例可以列举他们所有的属性名，这使得可以查看、遍历、或操作其中包含的属性集。
 */
public abstract class BaseTccPropertySource extends EnumerablePropertySource<Object> {

    @Getter
    private final String psm;
    @Getter
    private final String configSpace;
    @Getter
    private final Environment environment;
    @Getter
    private final String key;
    @Getter
    private final TccConfigClient tccConfigClient;
    @Getter
    private volatile Properties props;

    protected BaseTccPropertySource(String psm, String configSpace, Environment environment, String key) {
        // psm + configSpace + environment + key 构成唯一四元组
        super(psm + ":" + configSpace + ":" + environment.getName() + ":" + key, new Object());
        this.psm = psm;
        this.configSpace = configSpace;
        this.environment = environment;
        this.key = key;
        try {
            tccConfigClient = new TccConfigClient(psm, configSpace, environment);
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "Tcc load fail, psm=" + this.psm + ",configSpace=" + configSpace, e
            );
        }
    }

    public BaseTccPropertySource init() {
        // 获得最初配置
        String config = tccConfigClient.get(key);
        if (isBlank(config)) {
            throw new IllegalArgumentException("tcc:" + psm + ":" + configSpace + ":" + environment.name() + "加载内容为空");
        }
        try {
            this.props = loadProperties(config);
        } catch (IOException e) {
            throw new IllegalArgumentException("tcc:" + psm + ":" + configSpace + ":" + environment.name() + ",无法被正确加载", e);
        }
        return this;
    }

    // springBoot2x spring15x不同实现
    protected abstract Properties loadProperties(String config) throws IOException;

    public String[] getPropertyNames() {
        if (props == null) {
            return new String[0];
        } else {
            return props.keySet().toArray(new String[0]);
        }
    }

    public Object getProperty(String name) {
        return props.get(name);
    }

    protected static class StringResource extends AbstractResource {

        private final String content;

        public StringResource(String content) {
            this.content = content;
        }

        @Override
        public String getDescription() {
            return "";
        }

        @Override
        public InputStream getInputStream() {
            return new ByteArrayInputStream(content.getBytes(UTF_8));
        }
    }

    // get props
    Properties props() {
        return props;
    }

    // reset props
    void props(Properties props) {
        this.props = props;
    }
}
